﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.Specialized;
using System.Web;
using System.Reflection;
using System.Collections;

namespace GoogleMapsApiWebServices.Core
{
    public abstract class MapsApiRequest
    {
        protected abstract string BaseUri { get; }

        protected MessageFormat ResponseFormat { get; set; }

        [QueryStringParameter(IsRequired = true, QueryStringParameter = "sensor")]
        public bool DeviceHasLocationSensor { get; set; }

        public MapsApiRequest()
        {
            ResponseFormat = Core.MessageFormat.Json;
        }

        /// <summary>
        /// Allows derived classes to implement custom validation
        /// </summary>
        protected virtual void ValidateRequest()
        {
        }

        public virtual Uri ToUri()
        {
            ValidateRequest();

            NameValueCollection queryStringParams = GetQueryStringParameters();

            string baseUri = String.Format("{0}/{1}", BaseUri, ResponseFormat.ToString().ToLower());

            UriBuilder uri = new UriBuilder(baseUri);
            uri.Query = ToQueryString(queryStringParams);

            return uri.Uri;
        }

        public override string ToString()
        {
            return ToUri().ToString();
        }

        protected NameValueCollection GetQueryStringParameters()
        {
            NameValueCollection nvc = new NameValueCollection();

            Type derivedType = this.GetType();

            foreach (PropertyInfo property in derivedType.GetProperties())
            {
                object[] attributes = property.GetCustomAttributes(typeof(QueryStringParameterAttribute), false);

                if (attributes != null && attributes.Length > 0)
                {
                    object propertyValue = property.GetValue(this, null);

                    QueryStringParameterAttribute queryStringParam = attributes[0] as QueryStringParameterAttribute;

                    if (propertyValue == null && queryStringParam.IsRequired)
                        throw new Exception("Required attribute");

                    if (propertyValue != null)
                    {
                        if (propertyValue is IEnumerable)
                        {
                            IEnumerable enumerableProperty = propertyValue as IEnumerable;

                            IEnumerator enumerator = enumerableProperty.GetEnumerator();

                            StringBuilder queryStringBuilder = new StringBuilder();

                            while(enumerator.MoveNext())
                                queryStringBuilder.AppendFormat("{0}{1}", enumerator.Current.ToString(), queryStringParam.CollectionSeparator);

                            queryStringBuilder.Remove(queryStringBuilder.ToString().LastIndexOf(queryStringParam.CollectionSeparator), 1);

                            nvc.Add(queryStringParam.QueryStringParameter, queryStringBuilder.ToString().RemoveWhitespace());
                        }
                        else
                        {
                            nvc.Add(queryStringParam.QueryStringParameter, propertyValue.ToString().RemoveWhitespace());
                        }
                    }
                }
            }

            return nvc;
        }

        protected string ToQueryString(NameValueCollection nvc)
        {
            return string.Join("&", Array.ConvertAll(nvc.AllKeys, key => string.Format("{0}={1}", HttpUtility.UrlEncode(key), HttpUtility.UrlEncode(nvc[key])))).ToLower();
        }
    }
}
